﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Algoritmia.DataStructures.Maps
{
    public class IntKeyMap<TValue> : IMap<int,TValue>
    {
        protected int _capacity;
        protected TValue[] _values;
        protected bool[] _hasKey;
        protected int _count;

        private readonly KeysView<int, TValue> _keysView;
        private readonly ItemsView<int, TValue> _itemsView;
        private readonly ValuesView<int, TValue> _valuesView;

        public IntKeyMap(IEnumerable<KeyValuePair<int,TValue>> map=null,int capacity=0)
        {
            if (capacity < 0)
            {
                throw new ArgumentException("capacity must be positive");
            }

            if (map != null)
            {
                var tmpDict = map.ToDictionary(v => v.Key, v => v.Value);

                _capacity = Math.Max(tmpDict.Keys.Max() + 1,capacity);
                _values = new TValue[_capacity];
                _hasKey = new bool[_capacity];
                foreach (KeyValuePair<int, TValue> keyValuePair in tmpDict)
                {
                    _hasKey[keyValuePair.Key] = true;
                    _values[keyValuePair.Key] = keyValuePair.Value;
                    _count++;
                }
            }
            else
            {
                _capacity = capacity == 0 ? 128 : capacity;
                _values = new TValue[_capacity];
                _hasKey = new bool[_capacity];
            }

            _keysView = new KeysView<int, TValue>(this);
            _itemsView = new ItemsView<int, TValue>(this);
            _valuesView = new ValuesView<int, TValue>(this);
        }

        public virtual int Capacity
        {
            get 
            {
                return _capacity; 
            }
            set 
            {
                if (value <= 0)
                    throw new ArgumentException("Capacity has to be a positive number");
                _capacity = value;
                Array.Resize(ref _hasKey, _capacity);
                Array.Resize(ref _values, _capacity);
                _count = 0;
                for (int i = 0; i < _capacity; i++)
                    if (_hasKey[i]) _count++;
            }
        }

        #region IMap<TValue,int> Members

        public TValue this[int key]
        {
            get
            {
                if ( key < 0 || key >= _capacity)
                {
                    throw new IndexOutOfRangeException();
                 
                }
                if (!_hasKey[key])
                {
                    throw new KeyNotFoundException();
                }
                return _values[key];
            }
            set
            {
                try
                {
                    Add(key, value);
                }
                catch (ArgumentOutOfRangeException)
                {
                    throw new IndexOutOfRangeException();
                }
                
            }
        }

        public void Add( int key, TValue value)
        {
            if (key < 0 || key >= _capacity)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (!_hasKey[key])
            {
                _hasKey[key] = true;
                _count++;
            }
            _values[key] = value;
        }

        public bool Contains(int key)
        {
            return key >= 0 && key < _capacity && _hasKey[key];
        }

        public TValue Get(int key, TValue defaultValue)
        {
            if (key >= 0 && key < _capacity && _hasKey[key])
                return _values[key];
            return defaultValue;
        }

        public ICollectionView<int> Keys()
        {
            return _keysView;
        }

        public ICollectionView<KeyValuePair<int, TValue>> Items()
        {
            return _itemsView;
        }

        public bool Remove(int key)
        {
            if (key < 0 || key >= _capacity)
                return false;
            if (_hasKey[key])
            {
                _hasKey[key] = false;
                _count--;
                return true;
            }
            return false;
        }

        public TValue SetDefault(int key, TValue defaultValue)
        {
            if (Contains(key))
                return _values[key];
            try
            {
                this[key] = defaultValue;
            }
            catch (IndexOutOfRangeException)
            {
                throw new ArgumentOutOfRangeException();
            }
            return defaultValue;
        }

        public ICollectionView<TValue> Values()
        {
            return _valuesView;
        }

        #endregion

        #region IEnumerable<int> Members

        public IEnumerator<KeyValuePair<int, TValue>> GetEnumerator()
        {
            for (int i = 0; i < _capacity; i++)
                if (_hasKey[i])
                    yield return new KeyValuePair<int, TValue>(i, _values[i]);
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region ICountable Members

        public int Count
        {
            get { return _count; }
        }

        #endregion

        public override string ToString()
        {
            return String.Format("{0} <int,{1}> [{2}], capacity = {3} ", GetType().Name, typeof(TValue).Name,
                 string.Join(",", this), _capacity);
        }
    }
}
